﻿/*
 
Celerity: Sensory Overload
Copright 2012 by Adam Hill, David Gannon, Thomas Tamblyn & Patrick Ytting
Contact: adam.hill@outlook.com
Website: adamhill.org
Licence: MS-Pl Licence
  
 */

namespace Celerity.Input
{
    using Celerity.Data;
    using Microsoft.Xna.Framework;
    using Sensors;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Windows7.Multitouch;
    using Windows7.Multitouch.Win32Helper;
    using XIN = Microsoft.Xna.Framework.Input;

    public class InputModule
    {
        #region Instance Variables
        SensorProxy proxy;
        readonly TouchHandler touchHandler;
        private XIN.KeyboardState keyState;
        private XIN.GamePadState padState;
        public float playerJoystick;
        bool isFirstTimeDetectingMouse = true;
        float initialMouseX;
        float initialMouseY;
        Vector2 mouse;
        List<Vector2> mouseClicks; // Contains 0-1 click positions
        List<Vector2> touches; // Contains 0-n multitouch touches
        Vector2 currentMouseClick = Vector2.Zero;
        bool hasUnprocessedTouch;
        System.Drawing.Point lastTouchPoint;
        bool hasUnprocessedShake;
        bool aButtonUpProcessed = true;
        bool bButtonUpProcessed = true;
        bool backButtonUpProcessed = true;
        bool spaceKeyUpProcessed = true;
        bool escapeKeyUpProcessed = true;
        bool f6KeyUpProcessed = true;
        List<float> joystickInputs;
        #endregion /Input Variables

        #region Public Properties
        public XIN.MouseState MouseState;
        public bool DisplayMouse = false; // We set to true if the mouse moves
        public bool FireSmartbomb = false;
        public IEnumerable<Vector2> Interactions; // Contains combined touches and clicks
        public bool HasUnprocessedStartGame;
        public bool HasUnprocessedFlipMute;
        public bool HasUnprocessedBack;
        public bool HasUnprocessedExit;
        public bool HasAccel;
        public bool HasGyro;
        public bool HasLight;
        public bool HasIncline;
        public float AccelX;
        public float AccelY;
        public float AccelZ;
        public float GyroX;
        public float GyroY;
        public float GyroZ;
        public float Lux;
        public float InclineX;
        public float InclineY;
        public float InclineZ;
        #endregion /Public Properties

        #region Constructor
        public InputModule(IntPtr windowHandle)
        {
            // HACK: Multitouch hack
            touchHandler = Factory.CreateHandler<TouchHandler>(windowHandle);
            touchHandler.TouchUp += (s, e) =>
                {
                    lastTouchPoint = e.Location;
                    hasUnprocessedTouch = true;
                };
        }
        #endregion /Constructor

        #region Initialize
        public void Initialize()
        {
            GlobalGameStates.ControlAvailableGamepad = XIN.GamePad.GetCapabilities(0).IsConnected;
            GlobalGameStates.ControlAvailableKeyboard = true;
            playerJoystick = 0.0f;
            joystickInputs = new List<float>();
            touches = new List<Vector2>(10);
            mouseClicks = new List<Vector2>(1);
            Interactions = new List<Vector2>(11);
            proxy = new SensorProxy();
            proxy.LockOrientation();
            proxy.OnShaken += proxy_OnShaken;
        }
        #endregion /Initialize

        #region Sensor Events
        void proxy_OnShaken(object sender, EventArgs e)
        {
            if (GlobalGameStates.GameState == Data.GameState.InGame)
            {
                hasUnprocessedShake = true;
            }
            else
            {
                if (GlobalGameStates.GameState == Data.GameState.GameOver)
                {
                    HasUnprocessedBack = true;
                }
                else
                {
                    HasUnprocessedStartGame = true;
                }
            }
        }
        #endregion /Sensor Events

        #region Update
        public void Update()
        {
            UpdateTouches();
            UpdateMouse();
            UpdateSensors();
            Interactions = mouseClicks.Union(touches);
            keyState = XIN.Keyboard.GetState();
            padState = XIN.GamePad.GetState(0);
            HandleShipControls();

            // Ignore steering unless in-game
            if (GlobalGameStates.GameState != GameState.InGame)
            {
                playerJoystick = 0f;
            }
        }

        void UpdateTouches()
        {
            touches.Clear();
            if (hasUnprocessedTouch)
            {
                hasUnprocessedTouch = false;
                touches.Add(new Vector2(lastTouchPoint.X, lastTouchPoint.Y));
            }
        }

        void UpdateMouse()
        {
            // Read mouse data
            MouseState = XIN.Mouse.GetState();
            mouse.X = MouseState.X;
            mouse.Y = MouseState.Y;
            if (isFirstTimeDetectingMouse)
            {
                isFirstTimeDetectingMouse = false;
                initialMouseX = mouse.X;
                initialMouseY = mouse.Y;
            }

            // See if we should display the mouse
            if (!DisplayMouse)
            {
                if (MouseState.X != initialMouseX || MouseState.Y != initialMouseY)
                {
                    if (MouseState.LeftButton != XIN.ButtonState.Pressed)
                    {
                        DisplayMouse = true;
                    }
                }
            }

            // Check for Click
            if (MouseState.LeftButton == XIN.ButtonState.Pressed)
            {
                if (!mouseClicks.Any())
                {
                    currentMouseClick.X = MouseState.X;
                    currentMouseClick.Y = MouseState.Y;
                    mouseClicks.Add(currentMouseClick);
                }
            }
            else
            {
                mouseClicks.Clear();
            }
        }

        void UpdateSensors()
        {
            // Read last sensor reading values
            HasAccel = proxy.AccelAvailable;
            HasGyro = proxy.GyroAvailable;
            HasLight = proxy.LightAvailable;
            HasIncline = proxy.InclineAvailable;
            AccelX = (float)proxy.AccelX;
            AccelX = (float)proxy.AccelX;
            AccelX = (float)proxy.AccelX;
            GyroX = (float)proxy.GyroX;
            GyroX = (float)proxy.GyroY;
            GyroX = (float)proxy.GyroZ;
            Lux = (float)proxy.LightLux;
            InclineX = (float)proxy.InclineX;
            InclineY = (float)proxy.InclineY;
            InclineZ = (float)proxy.InclineZ;

            // Read misc sensor info
            GlobalGameStates.ControlAvailableInclinometer = HasIncline;
            if (hasUnprocessedShake)
            {
                hasUnprocessedShake = false;
                FireSmartbomb = true;
            }
        }
        #endregion /Update

        #region Handle Ship Controls
        void HandleShipControls()
        {
            // Init
            joystickInputs.Clear();
            playerJoystick = 0.0f;

            // Read inputs
            HandleKeyboard();
            HandleGamepad();
            HandleSensors();

            // Average inputs
            if (joystickInputs.Any())
            {
                playerJoystick = joystickInputs.Average();
            }
        }

        void HandleKeyboard()
        {
            if (keyState.IsKeyDown(XIN.Keys.Left))
            {
                playerJoystick = 1.0f;
                joystickInputs.Add(1);
            }

            if (keyState.IsKeyDown(XIN.Keys.Right))
            {
                playerJoystick = -1.0f;
                joystickInputs.Add(-1);
            }

            // Space
            if (keyState.IsKeyUp(XIN.Keys.Space))
            {
                if (!spaceKeyUpProcessed)
                {
                    spaceKeyUpProcessed = true;
                    if (GlobalGameStates.GameState == GameState.InGame)
                    {
                        hasUnprocessedShake = true;
                    }
                    else
                    {
                        if (GlobalGameStates.GameState == GameState.GameOver)
                        {
                            HasUnprocessedBack = true;
                        }
                        else
                        {
                            HasUnprocessedStartGame = true;
                        }
                    }
                }
            }

            if (keyState.IsKeyDown(XIN.Keys.Space))
            {
                spaceKeyUpProcessed = false;
            }

            // Escape
            if (keyState.IsKeyUp(XIN.Keys.Escape))
            {
                if (!escapeKeyUpProcessed)
                {
                    escapeKeyUpProcessed = true;
                    if (GlobalGameStates.GameState == GameState.Menu)
                    {
                        HasUnprocessedExit = true;
                    }
                    else
                    {
                        HasUnprocessedBack = true;
                    }
                }
            }

            if (keyState.IsKeyDown(XIN.Keys.Escape))
            {
                escapeKeyUpProcessed = false;
            }

            // F6
            if (keyState.IsKeyUp(XIN.Keys.F6))
            {
                if (!f6KeyUpProcessed)
                {
                    f6KeyUpProcessed = true;
                    HasUnprocessedFlipMute = true;
                }
            }

            if (keyState.IsKeyDown(XIN.Keys.F6))
            {
                f6KeyUpProcessed = false;
            }

            if (!keyState.IsKeyDown(XIN.Keys.Left) && !keyState.IsKeyDown(XIN.Keys.Right))
            {
                playerJoystick = 0.0f;
            }
        }

        void HandleGamepad()
        {
            GlobalGameStates.ControlAvailableGamepad = XIN.GamePad.GetCapabilities(0).IsConnected;
            if (GlobalGameStates.ControlAvailableGamepad)
            {
                playerJoystick = padState.ThumbSticks.Left.X;
                joystickInputs.Add(padState.ThumbSticks.Left.X * -1f);

                // Button A
                if (padState.Buttons.A == XIN.ButtonState.Released)
                {
                    if (!aButtonUpProcessed)
                    {
                        aButtonUpProcessed = true;
                        if (GlobalGameStates.GameState == GameState.InGame)
                        {
                            hasUnprocessedShake = true;
                        }
                        else
                        {
                            if (GlobalGameStates.GameState == GameState.GameOver)
                            {
                                HasUnprocessedBack = true;
                            }
                            else
                            {
                                HasUnprocessedStartGame = true;
                            }
                        }
                    }
                }
                if (padState.Buttons.A == XIN.ButtonState.Pressed)
                {
                    aButtonUpProcessed = false;
                }

                // Button Back
                if (padState.Buttons.Back == XIN.ButtonState.Released)
                {
                    if (!backButtonUpProcessed)
                    {
                        backButtonUpProcessed = true;
                        if (GlobalGameStates.GameState == GameState.Menu)
                        {
                            HasUnprocessedExit = true;
                        }
                        else
                        {
                            HasUnprocessedBack = true;
                        }
                    }
                }
                if (padState.Buttons.Back == XIN.ButtonState.Pressed)
                {
                    backButtonUpProcessed = false;
                }

                // Button B
                if (padState.Buttons.B == XIN.ButtonState.Released)
                {
                    if (!bButtonUpProcessed)
                    {
                        bButtonUpProcessed = true;
                        HasUnprocessedFlipMute = true;
                    }
                }
                if (padState.Buttons.B == XIN.ButtonState.Pressed)
                {
                    bButtonUpProcessed = false;
                }
            }
        }

        void HandleSensors()
        {
            if (proxy.InclineAvailable)
            {
                float clampAngle = CeleritySettings.SensorClampAngle;
                float halfClampAngle = clampAngle / 2f;
                float sensorSteer = InclineY;
                sensorSteer = Math.Min(halfClampAngle, sensorSteer);
                sensorSteer = Math.Max(-halfClampAngle, sensorSteer);
                playerJoystick = -1 * (-1f + 2f * ((sensorSteer + halfClampAngle) / clampAngle));
                joystickInputs.Add(playerJoystick);
            }
        }
        #endregion /Handle Ship Controls
    }
}
